//
//  graph.h
//  cobalt
//
//  Created by Nicolas Godbout on 2013-08-28.
//  Copyright (c) 2013 Nicolas Godbout. All rights reserved.
//

#ifndef __cobalt__graph__
#define __cobalt__graph__

#include "triple.h"

#include <exception>
#include <list>
#include <set>

namespace rdf {
    
    class Graph {
    public:
        typedef std::list<Triple> TripleList;
        
        Graph() noexcept;
        Graph(Graph const& other) noexcept;
        Graph(Graph&& other) noexcept;
        Graph& operator=(Graph const& other) noexcept;
        Graph& operator=(Graph&& other) noexcept;
        ~Graph() noexcept;
        
        size_t length() const noexcept;
        
        Graph&  add(const Triple&);
        Graph&  add(TripleList);
        
        Graph&  remove(TripleList) noexcept;
        
        Graph&  removeMatches(RDFNode const&,
                              RDFNode const&,
                              RDFNode const&) noexcept;
        
    //    std::vector<const Triple&>
    //    toArray() const;
        
        bool    some(TripleFilter) const noexcept;
        bool    every(TripleFilter) const noexcept;
        Graph&  filter(TripleFilter) noexcept;
        
        template <class T>
        void    forEach(T&) const noexcept;
        
        Graph   match(RDFNode const& subject,
                      RDFNode const& predicate,
                      RDFNode const& object) const noexcept;
        
        Graph   merge(const Graph&) const noexcept;
        
        Graph&  addAll(const Graph&) noexcept;
        
        std::list<const TripleAction&>
        actions() const noexcept;
        
        Graph&  addAction(const TripleAction& action, bool run=false) noexcept;
        
    private:
        struct GraphRecord {
            uint32_t    refCount;
            //        uint32_t    flags;
            uint32_t    size;
            // ## TODO: add a hash table to ensure triple uniqueness
            
            typedef std::multiset<Triple, Triple::lessST> sMap_t;
            typedef std::multiset<Triple, Triple::lessPT> pMap_t;
            typedef std::multiset<Triple, Triple::lessOT> oMap_t;
            
            sMap_t      sMap;
            pMap_t      pMap;
            oMap_t      oMap;
        };
        
        typedef GraphRecord::sMap_t::const_iterator triple_iterator;
        
        GraphRecord* graph;
    };
    
    inline
    Graph::Graph() noexcept
    : graph(new GraphRecord()) {
        graph->refCount = 1;
    }
    
    inline
    Graph::Graph(Graph const& other) noexcept
    : graph(other.graph) {
        graph->refCount++;
    }
    
    inline
    Graph::Graph(Graph&& other) noexcept
    : graph(other.graph) {
        other.graph = nullptr;
    }
    
    inline Graph&
    Graph::operator=(Graph const& other) noexcept {
        if ( --graph->refCount == 0 )
            delete graph;
        graph = other.graph;
        graph->refCount++;
        return *this;
    }
    
    inline Graph&
    Graph::operator=(Graph&& other) noexcept {
        GraphRecord* tmp = graph;
        graph = other.graph;
        other.graph = tmp;
        return *this;
    }
    
    inline
    Graph::~Graph() noexcept {
        if ( graph && --graph->refCount == 0 )
            delete graph;
    }
    
    inline size_t
    Graph::length() const noexcept {
        return graph->size;
    }
    
    
/*
    template <class T>
    void
    Graph::forEach(T& callback) const noexcept {
        static_assert(std::is_base_of<TripleCallback,T>::value, "Argument of Graph::forEach must be a subclass of TripleCallback.");
        
        setSPO_t::const_iterator triple = setSPO.cbegin();
        setSPO_t::const_iterator end = setSPO.cend();
        
        while ( triple != end )
            callback.run( *triple++ , *this);
    }
*/
} // namespace rdf

#endif /* defined(__cobalt__graph__) */
